Skip to content

feat:Add support for installing native Android game builds (Steam Frame) - #1742

Open
moi952 wants to merge 6 commits into
utkarshdalal:masterfrom
moi952:feat/android-steam-version
Open

feat:Add support for installing native Android game builds (Steam Frame)#1742
moi952 wants to merge 6 commits into
utkarshdalal:masterfrom
moi952:feat/android-steam-version

Conversation

@moi952

@moi952 moi952 commented Jul 19, 2026

Copy link
Copy Markdown

Steam now lets some games be installed as a native Android app instead of the usual Windows build run through Wine (ahead of Valve's Steam Frame headset). This adds a per-game Version choice in Edit Container: picking Android downloads, installs, and launches the game as a real Android app, bypassing Wine entirely for that game.

Build : https://www.transfernow.net/dl/20260723bHOQ5wLl

Description

Some games on Steam now ship a native Android build alongside their usual Windows build — this is in preparation for Valve's upcoming Steam Frame VR headset, but it also just works as a regular Android app on any device.

This PR adds the ability to pick that Android version instead of the Windows one, per game:

New "Version" dropdown in Edit Container (only shows up for games that actually have an Android build available).
Picking "Android" downloads the right files, installs the app through Android's normal system installer, and launches it directly — no Wine, no Proton, no Box64 involved at all for that game.
Uninstalling now also removes the installed Android app itself, not just the downloaded files.
Everything defaults to the current Windows/Wine behavior — nothing changes for existing games unless you explicitly pick Android for one.
One thing worth knowing about the build variants: this only works on the Legacy builds (legacy / legacyXr) for now. The Modern builds (modern / modernXr) intentionally have the "install/uninstall other apps" permissions stripped out, because the Meta Horizon Store rejected a previous submission over exactly those permissions. Since modernXr is the variant meant for store distribution, adding this feature there would risk the same rejection again — so for now, Android games are a Legacy-only feature. Worth a deliberate call later if we want this on Modern too.

New android filter

Recording

Type of Change

  • Bug fix
  • Performance / stability improvement
  • Compatibility improvements
  • Other (requires prior approval)

Checklist

  • If I have access to #code-changes, I have discussed this change there and it has been green-lighted. If I do not have access, I have still provided clear context in this PR. If I skip both, I accept that this change may face delays in review, may not be reviewed at all, or may be closed.
  • This change aligns with the current project scope (core functionality, stability, or performance). If not, it has been explicitly approved beforehand.
  • I have attached a recording of the change.
  • I have read and agree to the contribution guidelines in CONTRIBUTING.md.

Summary by cubic

Adds per-game support for installing and launching native Android (Steam Frame/Lepton) builds. You can choose Android per game; it skips Wine, auto-prompts system install after download, and the UI updates live.

  • New Features

    • Version picker in Edit Container for games with an Android depot; Android mode disables non-General tabs and Wine fields; dialog opens without blocking the UI.
    • Platform-aware depot filtering, language selection, and size; Android flow auto-starts the system installer after download and launches the installed app; Play only appears once the system app is installed.
    • Library: new “Android version available” filter; hidden on modern/modernXr and stripped if persisted; non‑Steam sources are excluded when the Android filter is active.
  • Bug Fixes

    • Android containers fully bypass Wine, including pre-launch and temporary overrides; launch path short-circuits to native.
    • Uninstall prompts to remove the Android app first and honors cancel; staged APKs are cleaned up; no more freeze on cancel; immediate install prompt uses a safe cache path.
    • UI refreshes on package add/remove and on platform changes; store/install size recompute with the selected platform; permissions: REQUEST_DELETE_PACKAGES kept in legacy, removed in modern/modernXr.

Written for commit 654e7d9. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added native Android game support with per-container platform selection (Windows/Android).
    • Added Android APK install/launch and uninstall with install/launch failure and uninstall-cancel feedback.
  • Improvements

    • Depot availability, language, and download sizing are now platform-aware (Android vs Windows).
    • Library filtering and install-size refresh are improved; Android platforms restrict advanced configuration tabs/fields.
  • Permissions/Packaging

    • Updated Android manifests to adjust delete-package permission handling.
  • UI/Localization

    • Added Android platform UI strings (including French).
  • Tests

    • Added unit tests for Android OS parsing and bitmask behavior.

@moi952
moi952 requested a review from utkarshdalal as a code owner July 19, 2026 16:09
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This change adds native Android game support through Android depot compatibility, persistent container platform selection, APK installation and launch handling, Android-specific UI behavior, and platform-aware download and uninstall flows.

Changes

Android platform support

Layer / File(s) Summary
Platform contracts and persistence
app/src/main/java/app/gamenative/{data,enums}/..., app/src/main/java/com/winlator/container/..., app/src/main/java/app/gamenative/utils/ContainerUtils.kt, app/src/test/java/app/gamenative/enums/OSTest.kt
Adds Android OS/depot compatibility and persists Windows or Android platform settings in containers, Compose state, and duplicated containers, with enum parsing and bitmask tests.
Platform-aware depot selection and downloading
app/src/main/java/app/gamenative/service/{SteamService.kt,DownloadService.kt}, app/src/main/java/app/gamenative/utils/SteamUtils.kt, app/src/main/java/app/gamenative/ui/component/dialog/GameManagerDialog.kt
Threads Android platform selection through depot eligibility, language resolution, size calculation, DLC selection, downloading, and post-install handling.
Android APK installation and package handling
app/src/main/java/app/gamenative/utils/{AndroidGameLauncher.kt,UpdateInstaller.kt}, app/src/main/res/{xml,file_provider_paths.xml,values*/strings.xml}, app/src/{main,modern,modernXr}/AndroidManifest.xml
Resolves downloaded APK packages, copies OBB files, starts installation or launch, requests uninstall, and updates installer, provider, permission, and status-message configuration.
Container platform configuration UI
app/src/main/java/app/gamenative/ui/component/dialog/..., app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt, app/src/main/res/values*/strings.xml
Adds platform selection when Android depots exist, restricts Android containers to General settings, disables Windows-specific controls, and supplies localized messages.
Android launch and installation lifecycle
app/src/main/java/app/gamenative/ui/{PluviaMain.kt,model/MainViewModel.kt}, app/src/main/java/app/gamenative/ui/screen/library/appscreen/{BaseAppScreen.kt,SteamAppScreen.kt}, app/src/main/java/app/gamenative/ui/model/LibraryViewModel.kt, app/src/main/java/app/gamenative/ui/enums/AppFilter.kt
Short-circuits Windows launch preparation, invokes native installation and launch, refreshes package state, supports platform changes and uninstall requests, and adds Android-version filtering.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Player
  participant MainViewModel
  participant SteamService
  participant AndroidGameLauncher
  participant UpdateInstaller
  Player->>MainViewModel: launch Android container
  MainViewModel->>AndroidGameLauncher: installAndLaunch(gameId)
  AndroidGameLauncher->>UpdateInstaller: install APK when package is absent
  AndroidGameLauncher-->>MainViewModel: InstallStarted, Launched, or Failed
  MainViewModel-->>Player: show installation or launch status
Loading

Possibly related PRs

Suggested reviewers: utkarshdalal

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly matches the main change: adding native Android game build installs.
Description check ✅ Passed The description mostly matches the template, with clear context, type, and checklist, but the Recording section is empty.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt (2)

55-66: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

OBB placement failure is silently swallowed.

copyObbFiles only logs a warning on failure; installAndLaunch calls it and proceeds with UpdateInstaller.installApk regardless of the outcome. Per the kdoc, this failure mode ("games that need their OBB will fail to start") is expected on Android 11+ without broad storage access, but the user gets zero indication why the freshly-installed game won't launch — this is partial error propagation into a confusing dead end.

Returning a status from copyObbFiles and reflecting it in Result (or at least a distinct log/snackbar) would let the caller surface something more actionable than a silent black-box failure.

♻️ Sketch
-    private fun copyObbFiles(gameId: Int, packageName: String) {
+    private fun copyObbFiles(gameId: Int, packageName: String): Boolean {
         val obbFiles = findObbFiles(gameId)
-        if (obbFiles.isEmpty()) return
+        if (obbFiles.isEmpty()) return true
         try {
             val obbRoot = File(Environment.getExternalStorageDirectory(), "Android/obb/$packageName")
             obbRoot.mkdirs()
             obbFiles.forEach { src -> src.copyTo(File(obbRoot, src.name), overwrite = true) }
             Timber.tag(TAG).i("Copied ${obbFiles.size} OBB file(s) to ${obbRoot.absolutePath}")
+            return true
         } catch (e: Exception) {
             Timber.tag(TAG).w(e, "Could not place OBB files for $packageName (needs broader storage access on Android 11+)")
+            return false
         }
     }

Also applies to: 102-106

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt` around lines
55 - 66, Update copyObbFiles to return an explicit success/failure status
instead of swallowing placement errors, and have installAndLaunch inspect that
result before or alongside UpdateInstaller.installApk. Surface a distinct
actionable failure through the existing Result or user-facing error path, while
preserving the current successful-copy behavior and warning details.

20-30: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Repeated disk walk + PackageManager parse with no caching.

findApkFile/findObbFiles recursively walk the entire game install directory, and resolvePackageName/isGameInstalled re-run this walk plus getPackageArchiveInfo parsing on every call. These are non-suspend, synchronous functions; SteamAppScreen.isInstalled() and getAndroidPackageName() call into them directly and are the kind of method typically invoked repeatedly from UI/state-observation code (e.g. on every onStateChanged() in observeGameState). For large asset trees this becomes a non-trivial, repeated I/O cost on every check.

Also, if a game directory ever contains more than one .apk (leftover from an update, etc.), firstOrNull picks an arbitrary one with no validation.

Consider memoizing the resolved package name per gameId (invalidate when a new APK is installed/re-downloaded).

♻️ Example caching approach
+    private val packageNameCache = mutableMapOf<Int, String?>()
+
     fun resolvePackageName(context: Context, gameId: Int): String? {
-        val apk = findApkFile(gameId) ?: return null
-        return getApkPackageName(context, apk)
+        return packageNameCache.getOrPut(gameId) {
+            val apk = findApkFile(gameId) ?: return@getOrPut null
+            getApkPackageName(context, apk)
+        }
     }
+
+    /** Call after a fresh download/uninstall so a stale cached name isn't reused. */
+    fun invalidateCache(gameId: Int) {
+        packageNameCache.remove(gameId)
+    }

Also applies to: 68-78

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt` around lines
20 - 30, Memoize the resolved package name per gameId in the AndroidGameLauncher
flow, reusing the cached value from resolvePackageName/isGameInstalled instead
of repeating findApkFile, findObbFiles, and getPackageArchiveInfo during UI
state checks. Invalidate the corresponding cache entry whenever APK installation
or re-download completes. Replace arbitrary firstOrNull APK selection with
deterministic validation that handles multiple APKs safely before resolving the
package.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/app/gamenative/service/SteamService.kt`:
- Around line 1422-1427: Update SteamService.isAndroidPlatform to avoid
force-unwrapping instance when creating ContainerManager. Safely access the
service context and return false when instance is null, while preserving the
existing platform comparison when the service is running.

In
`@app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt`:
- Around line 1248-1254: Update the onPreviewKeyEvent handler’s
Key.ButtonR1/Key.ButtonR2 and Key.ButtonL1/Key.ButtonL2 branches to ignore
shoulder-button navigation when isAndroidPlatform is true, while preserving
event consumption and existing tab cycling for non-Android platforms.

In
`@app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt`:
- Around line 872-897: Update saveContainerConfig so that when platformChanged
and the existing container platform is Android and the app is installed, call
AndroidGameLauncher.requestUninstall before SteamService.deleteApp; preserve the
existing reinstall and redownload flow for other platform switches.

In `@app/src/main/res/xml/file_provider_paths.xml`:
- Around line 6-9: Replace the broad root-path entry in the FileProvider paths
configuration with narrowly scoped provider paths for the internal app data
directory, external app directory, and external SD-volume install roots used by
SteamService.allInstallPaths. Remove the catch-all access while preserving
support for downloaded Android game APK installation.

---

Nitpick comments:
In `@app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt`:
- Around line 55-66: Update copyObbFiles to return an explicit success/failure
status instead of swallowing placement errors, and have installAndLaunch inspect
that result before or alongside UpdateInstaller.installApk. Surface a distinct
actionable failure through the existing Result or user-facing error path, while
preserving the current successful-copy behavior and warning details.
- Around line 20-30: Memoize the resolved package name per gameId in the
AndroidGameLauncher flow, reusing the cached value from
resolvePackageName/isGameInstalled instead of repeating findApkFile,
findObbFiles, and getPackageArchiveInfo during UI state checks. Invalidate the
corresponding cache entry whenever APK installation or re-download completes.
Replace arbitrary firstOrNull APK selection with deterministic validation that
handles multiple APKs safely before resolving the package.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 65e44b76-89db-40cc-9924-61b27bd95227

📥 Commits

Reviewing files that changed from the base of the PR and between c70ad6e and 2707a79.

📒 Files selected for processing (23)
  • app/src/main/AndroidManifest.xml
  • app/src/main/java/app/gamenative/data/DepotInfo.kt
  • app/src/main/java/app/gamenative/enums/OS.kt
  • app/src/main/java/app/gamenative/service/DownloadService.kt
  • app/src/main/java/app/gamenative/service/SteamService.kt
  • app/src/main/java/app/gamenative/ui/PluviaMain.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigState.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/GameManagerDialog.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/GeneralTab.kt
  • app/src/main/java/app/gamenative/ui/model/MainViewModel.kt
  • app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt
  • app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt
  • app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt
  • app/src/main/java/app/gamenative/utils/ContainerUtils.kt
  • app/src/main/java/app/gamenative/utils/UpdateInstaller.kt
  • app/src/main/java/com/winlator/container/Container.java
  • app/src/main/java/com/winlator/container/ContainerData.kt
  • app/src/main/res/values-fr/strings.xml
  • app/src/main/res/values/strings.xml
  • app/src/main/res/xml/file_provider_paths.xml
  • app/src/modern/AndroidManifest.xml
  • app/src/modernXr/AndroidManifest.xml

Comment thread app/src/main/java/app/gamenative/service/SteamService.kt
Comment thread app/src/main/res/xml/file_provider_paths.xml Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 23 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/src/main/java/app/gamenative/enums/OS.kt">

<violation number="1" location="app/src/main/java/app/gamenative/enums/OS.kt:11">
P3: Android depot selection depends on this enum being recognized both from Steam's `"android"` metadata and from the persisted bitmask, but neither path has regression coverage. A focused test for `OS.from("android")` and `OS.from(OS.code(EnumSet.of(OS.android)))` would protect the new opt-in flow from parser or serialization regressions.</violation>
</file>

Tip: cubic used a learning from your PR history. Let your coding agent read cubic learnings directly with the cubic MCP.

Re-trigger cubic

Comment thread app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt Outdated
Comment thread app/src/main/java/app/gamenative/service/SteamService.kt
Comment thread app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt
Comment thread app/src/main/java/app/gamenative/ui/PluviaMain.kt
Comment thread app/src/main/java/app/gamenative/service/DownloadService.kt
Comment thread app/src/main/res/values-fr/strings.xml Outdated
Comment thread app/src/main/res/values-fr/strings.xml Outdated
Comment thread app/src/main/res/values/strings.xml Outdated
Comment thread app/src/main/java/app/gamenative/ui/model/MainViewModel.kt Outdated
@moi952

moi952 commented Jul 19, 2026

Copy link
Copy Markdown
Author

@utkarshdalal

While testing, we found that if you tapped "Uninstall" on a Steam Frame / Lepton (Android) game and then cancelled the system's confirmation popup, GameNative would get stuck showing a "Deleting..." popup — and it would follow you to whatever game page you opened next, effectively freezing that part of the app for a couple of minutes.

Two things were wrong:

GameNative had no way to know right away that you'd hit "Cancel", it could only wait a fixed 2 minutes to give up, so cancelling looked like a freeze.
The "Deleting..." popup wasn't tied to the specific game being deleted, so it showed up on any game page you visited in the meantime, not just the one you were uninstalling.
Both are fixed: cancelling (or confirming) the system popup is now detected instantly, and the "Deleting..." popup only ever shows for the game actually being removed.

Metro Awakening works, but maybe they have a license verification system, or perhaps they released a version that prevents play until a certain date? I don't know, but in any case, THE CONTROLLERS WORK!!!
Steam APK
https://discord.com/channels/1378308569287622737/1524497941040074812/1528453668209295450

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt (1)

84-95: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Prefer externalCacheDir for large game APKs to prevent storage exhaustion.

Game APKs can be gigabytes in size. Staging them in context.cacheDir (internal storage) risks filling up the device's internal storage and causing OutOfSpaceException. Consider preferring context.externalCacheDir, falling back to context.cacheDir if it's unavailable.

♻️ Proposed fix
     private fun stageApkForInstall(context: Context, gameId: Int, apk: File): File? {
         return try {
-            val stagingDir = File(context.cacheDir, "android_game_installs").apply { mkdirs() }
+            val cacheBase = context.externalCacheDir ?: context.cacheDir
+            val stagingDir = File(cacheBase, "android_game_installs").apply { mkdirs() }
             val staged = File(stagingDir, "$gameId.apk")
             apk.copyTo(staged, overwrite = true)
             staged
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt` around lines
84 - 95, Update stageApkForInstall to use context.externalCacheDir as the
staging directory when available, with context.cacheDir as the fallback, while
preserving the existing android_game_installs subdirectory, copy behavior, and
error handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt`:
- Around line 1417-1426: Update the nested ExposedDropdownMenu in the dropdown
component to use expanded && enabled, and reset expanded whenever enabled
becomes false so a disabled dropdown cannot retain or reopen its menu.

In `@app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt`:
- Around line 174-178: Update the activity lookup in the uninstall flow around
requestUninstall to unwrap ContextWrapper instances recursively until locating
the underlying ComponentActivity. Use the unwrapped activity for prompting,
while preserving the existing error log and false return when no
ComponentActivity can be found.

---

Nitpick comments:
In `@app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt`:
- Around line 84-95: Update stageApkForInstall to use context.externalCacheDir
as the staging directory when available, with context.cacheDir as the fallback,
while preserving the existing android_game_installs subdirectory, copy behavior,
and error handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5a5b8c7d-fca9-4671-8975-4f05b7ef170f

📥 Commits

Reviewing files that changed from the base of the PR and between e46daf5 and 1e97652.

📒 Files selected for processing (15)
  • app/src/main/java/app/gamenative/service/SteamService.kt
  • app/src/main/java/app/gamenative/ui/PluviaMain.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/ContainerConfigDialog.kt
  • app/src/main/java/app/gamenative/ui/component/dialog/GeneralTab.kt
  • app/src/main/java/app/gamenative/ui/model/MainViewModel.kt
  • app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt
  • app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt
  • app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt
  • app/src/main/java/app/gamenative/utils/SteamUtils.kt
  • app/src/main/java/app/gamenative/utils/UpdateInstaller.kt
  • app/src/main/java/com/winlator/container/ContainerManager.java
  • app/src/main/res/values-fr/strings.xml
  • app/src/main/res/values/strings.xml
  • app/src/main/res/xml/file_provider_paths.xml
  • app/src/test/java/app/gamenative/enums/OSTest.kt
🚧 Files skipped from review as they are similar to previous changes (8)
  • app/src/main/res/values/strings.xml
  • app/src/main/java/app/gamenative/ui/PluviaMain.kt
  • app/src/main/res/values-fr/strings.xml
  • app/src/main/java/app/gamenative/ui/component/dialog/GeneralTab.kt
  • app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt
  • app/src/main/java/app/gamenative/ui/model/MainViewModel.kt
  • app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt
  • app/src/main/java/app/gamenative/service/SteamService.kt

Comment thread app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 15 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/src/main/java/app/gamenative/service/SteamService.kt">

<violation number="1" location="app/src/main/java/app/gamenative/service/SteamService.kt:951">
P3: Android depot selection now depends on this language-resolution flag, but the test suite has no Android-mode coverage for preferred-language, English fallback, or neutral-depot cases, allowing a later change to silently produce zero Android depots. A focused Android parameterized/helper path in `SteamUtilsDepotLanguageTest` would lock down these three call-site changes.

(Based on your team's feedback about tests for complex depot-selection logic.) [FEEDBACK_USED]</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread app/src/main/java/app/gamenative/ui/model/MainViewModel.kt
Comment thread app/src/main/java/app/gamenative/utils/UpdateInstaller.kt
Comment thread app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt Outdated
val dlcAppIdsWithSingleDepots = getDlcAppIdsWithSingleDepot(depots)
val effectiveLanguage = SteamUtils.effectiveDepotLanguage(
depots, preferredLanguage, ownedDlc, licensedDepotIds, hasSteamUnlockedBranch,
depots, preferredLanguage, ownedDlc, licensedDepotIds, hasSteamUnlockedBranch, wantAndroid,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Android depot selection now depends on this language-resolution flag, but the test suite has no Android-mode coverage for preferred-language, English fallback, or neutral-depot cases, allowing a later change to silently produce zero Android depots. A focused Android parameterized/helper path in SteamUtilsDepotLanguageTest would lock down these three call-site changes.

(Based on your team's feedback about tests for complex depot-selection logic.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/java/app/gamenative/service/SteamService.kt, line 951:

<comment>Android depot selection now depends on this language-resolution flag, but the test suite has no Android-mode coverage for preferred-language, English fallback, or neutral-depot cases, allowing a later change to silently produce zero Android depots. A focused Android parameterized/helper path in `SteamUtilsDepotLanguageTest` would lock down these three call-site changes.

(Based on your team's feedback about tests for complex depot-selection logic.) </comment>

<file context>
@@ -948,7 +948,7 @@ class SteamService : Service(), IChallengeUrlChanged {
             val dlcAppIdsWithSingleDepots = getDlcAppIdsWithSingleDepot(depots)
             val effectiveLanguage = SteamUtils.effectiveDepotLanguage(
-                depots, preferredLanguage, ownedDlc, licensedDepotIds, hasSteamUnlockedBranch,
+                depots, preferredLanguage, ownedDlc, licensedDepotIds, hasSteamUnlockedBranch, wantAndroid,
             )
             val eligible = eligibleDepots(depots, effectiveLanguage, ownedDlc, licensedDepotIds, wantAndroid)
</file context>

Comment thread app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt (1)

66-76: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Propagate OBB placement failures instead of reporting installation success.

copyObbFiles() swallows copy/storage failures, but installAndLaunch() still returns InstallStarted. Games requiring OBB data can therefore become installed-but-unlaunchable; subsequent launches skip OBB copying because the package is already installed. Android 11+ scoped storage further restricts direct writes outside app-specific storage. (developer.android.com)

Return a success/failure result from OBB placement, fail or clearly report the installation when placement fails, and provide a repair path for already-installed packages with missing OBBs.

Also applies to: 145-149

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt` around lines
66 - 76, Update copyObbFiles() to return an explicit success/failure result
instead of swallowing placement exceptions, and have installAndLaunch()
propagate that failure rather than returning InstallStarted. Ensure
already-installed packages with missing OBB data still invoke the
placement/repair flow before launch, and clearly report failure when Android
storage restrictions prevent copying.

Source: MCP tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt`:
- Around line 66-76: Update copyObbFiles() to return an explicit success/failure
result instead of swallowing placement exceptions, and have installAndLaunch()
propagate that failure rather than returning InstallStarted. Ensure
already-installed packages with missing OBB data still invoke the
placement/repair flow before launch, and clearly report failure when Android
storage restrictions prevent copying.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bb6d8319-255a-4ad3-9103-5fded78b6795

📥 Commits

Reviewing files that changed from the base of the PR and between 1e97652 and 525708d.

📒 Files selected for processing (5)
  • app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt
  • app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt
  • app/src/main/java/app/gamenative/utils/AndroidGameLauncher.kt
  • app/src/main/java/app/gamenative/utils/ContainerUtils.kt
  • app/src/main/java/app/gamenative/utils/UpdateInstaller.kt
🚧 Files skipped from review as they are similar to previous changes (4)
  • app/src/main/java/app/gamenative/utils/ContainerUtils.kt
  • app/src/main/java/app/gamenative/utils/UpdateInstaller.kt
  • app/src/main/java/app/gamenative/ui/screen/library/appscreen/BaseAppScreen.kt
  • app/src/main/java/app/gamenative/ui/screen/library/appscreen/SteamAppScreen.kt

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 8 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/src/main/java/app/gamenative/ui/screen/library/components/LibraryOptionsPanel.kt">

<violation number="1" location="app/src/main/java/app/gamenative/ui/screen/library/components/LibraryOptionsPanel.kt:276">
P2: A user upgrading from a Legacy build with the Android filter enabled can retain that persisted flag on a Modern build, where this line removes the only visible way to disable it. The library can then remain restricted to Android-compatible Steam depots (or appear empty) even though native Android installation is unavailable; the Modern initialization path should clear or ignore `AppFilter.ANDROID` when restoring/applying filters.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

add(AppFilter.FIVE_STAR)
add(AppFilter.FIVE_STAR_GPU)
add(AppFilter.PROVEN_GPU)
if (!BuildConfig.MODERN_ANDROID) add(AppFilter.ANDROID)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: A user upgrading from a Legacy build with the Android filter enabled can retain that persisted flag on a Modern build, where this line removes the only visible way to disable it. The library can then remain restricted to Android-compatible Steam depots (or appear empty) even though native Android installation is unavailable; the Modern initialization path should clear or ignore AppFilter.ANDROID when restoring/applying filters.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/java/app/gamenative/ui/screen/library/components/LibraryOptionsPanel.kt, line 276:

<comment>A user upgrading from a Legacy build with the Android filter enabled can retain that persisted flag on a Modern build, where this line removes the only visible way to disable it. The library can then remain restricted to Android-compatible Steam depots (or appear empty) even though native Android installation is unavailable; the Modern initialization path should clear or ignore `AppFilter.ANDROID` when restoring/applying filters.</comment>

<file context>
@@ -259,18 +260,24 @@ fun LibraryOptionsPanel(
+                                    add(AppFilter.FIVE_STAR)
+                                    add(AppFilter.FIVE_STAR_GPU)
+                                    add(AppFilter.PROVEN_GPU)
+                                    if (!BuildConfig.MODERN_ANDROID) add(AppFilter.ANDROID)
+                                }
+                            }
</file context>

@moi952

moi952 commented Jul 19, 2026

Copy link
Copy Markdown
Author

@utkarshdalal I think I’ve addressed all the relevant points raised by Cubic AI.
Everything is working perfectly.

moi952 added 6 commits July 23, 2026 11:43
Steam now lets some games be installed as a native Android app instead
of the usual Windows build run through Wine (ahead of Valve's Steam
Frame headset). This adds a per-game Version choice in Edit Container:
picking Android downloads, installs, and launches the game as a real
Android app, bypassing Wine entirely for that game.
…, add Android-mode depot language test coverage
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant